/*!
 * Copyright (c) 2010 Jacob Essex
 *
 * Dual licenced under MIT or GPL 3 licneces
 * 
 * See the GPL3 or MIT files in the main directory for details
 */

/**
 * Ask the user for a list of their favoured tags. I did look at precalculating but it didn't work
 *
 * Where there is only one of those tags in that question, consider all other tags a subtag of that major tag
 *  for now reject all items with two major tags
 *
 * Now the stats part.
 *  For a given question, try to find the major tag. This search should include the question title
 *
 *  Ignoring the major tag, analyse the other tags. 
 *      Consider each tag and see how well the user did at answering questions that each tag
 *
 *
 *      Treat all unknown tags as having a usage of 0 
 *
 *      drop the worst 1/3 of the tags  
 *
 * Allow for crossover of well defined tags ... a bit
 *      
 */

//TODO huge amount of duplicated code in here...

function Fuzzy(majorTags, classBoundary){

    /**
     * @return 0 for bad, 1 for good
     */
    var classify = function(answer){
        return ( answer.UpVotes > classBoundary ) ? 1 : 0; //check return types
    }

    var tagCount = {};
    var majorTagDetails = {}
    var tagDetails = {};
    for ( var i = 0; i < majorTags.length; i++ ){
        majorTagDetails[majorTags[i]] = {}
    }

    this.setTagCount = function(t, c){
        tagCount[t] = c;
    }

    this.train = function(answer){
        var c = classify(answer);
        var mt = [];

        //add all tags to tagDetails 
        for ( var i = 0; i < answer.QuestionTags.length; i++ ){
            var t = answer.QuestionTags[i]; 
            if ( tagDetails[t] === undefined ){
                tagDetails[t] = [0, 0];
            }
            tagDetails[t][c]++;
        }


        //find the major tags
        for ( var i = 0; i < answer.QuestionTags.length; i++ ){
            var t = answer.QuestionTags[i]; 
            if ( majorTags.indexOf(t) !== -1 ){
                mt.push(t);
            }
        }

        //add all other tags to the major tag
        for ( var i = 0; i < answer.QuestionTags.length; i++ ){
            var t = answer.QuestionTags[i]; 
            if ( majorTags.indexOf(t) === -1 ){
                for ( var j = 0; j < mt.length; j++ ){
                    if ( majorTagDetails[mt[j]][t] === undefined ){
                        majorTagDetails[mt[j]][t] = [0, 0];
                    }
                    majorTagDetails[mt[j]][t][c]++;
                }
            }
        }

    }


    
    this.guess = function(question){
        //get the major tags
        var mt = [];
        for ( var i = 0; i < question.QuestionTags.length; i++ ){
            var t = question.QuestionTags[i]; 
            if ( majorTags.indexOf(t) !== -1 ){
                mt.push(t);
            }
        }
        if ( question.InferredTags != undefined ){
            for ( var i = 0; i < question.InferredTags.length; i++ ){
                var t = question.QuestionTags[i]; 
                if ( majorTags.indexOf(t) !== -1 && mt.indexOf(t) == -1 ){
                    mt.push(t);
                }
            }
        }

        var getSingleTagProb = function(data, tag, w){
            if ( w == undefined ){
                w = 0.15;
            }
            if ( data[t] === undefined ){
                return 0.0001;
            }else{
                var countGood = data[t][1] ;
                var countBad = data[t][0] ;
                if ( countGood + countBad > 5 ){
                    return  countGood / (countBad + countGood ) ;
                }else{
                    return w;
                }
            }
        }

        var weightByCount = function(probs){
            probs.sort(function(a,b){
                var ac = tagCount[a] == undefined ? 0 : tagCount[a];
                var bc = tagCount[b] == undefined ? 0 : tagCount[b];
                return bc-ac;
            });

            var diff = (0.25)/probs.length;
            var cur = 1;
            for ( var i = 1; i < probs.count; i++ ){
                if ( tagCount[probs[i-1]] != tagCount[probs[i]] ){
                    cur -= diff;
                    probs[i] *= cur;
                }
            }
            return probs; 
        }

        //consider the rest of the tags
        //
        //the things we should consider
        var mtProbs = [];
        for ( var i = 0; i < mt.length; i++ ){
            var probs = [1]; //major tag, so start with prob of 1
            for ( var j = 0; j < question.QuestionTags.length; j++ ){
                var tagProb = 1; 
                var t = question.QuestionTags[j]; 
                if ( majorTags.indexOf(t) === -1 ){ //for each tag that isn't a major tag
                    tagProb *= getSingleTagProb(majorTagDetails[mt[i]], t);
                    probs.push(tagProb);
                }
            }
            //probs = weightByCount(probs);

            var avg = 0;
            for ( var j = 0; j < probs.length; j++ ){
                avg += probs[j];
            }
            mtProbs.push(avg/probs.length);
        }
        var totalAvg = 0;
        for ( var i = 0; i < mtProbs.length; i++ ){
            totalAvg += mtProbs[i];
        }
        totalAvg /= mtProbs.length ? mtProbs.length : 1;

        //now consider overlap. ie. if someone knows about http in php, they may also have a clue about http in c++
        var simAvg = (function(){
            var probs = [];
            for ( var i = 0; i < question.QuestionTags.length; i++ ){
                var t = question.QuestionTags[i];
                var tagProb = getSingleTagProb(tagDetails, t, 0.01);
                probs.push(tagProb);
            }
            //probs = weightByCount(probs);
            var avg = 0;
            for ( var j = 0; j < probs.length; j++ ){
                avg += probs[j];
            }
            avg /= probs.length ? probs.length : 1;
            return avg
        })();

        return (totalAvg * 1.6 + simAvg*0.4)/2

        

    }

}
